package com.piece.core.authentication.handler;

import com.piece.core.web.authority.token.TokenServices;
import com.piece.core.framework.constant.CookieConstants;
import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.constant.HeaderConstants;
import com.piece.core.framework.constant.ExceptionAuthConstants;
import com.piece.core.framework.constant.SecurityConstants;
import com.piece.core.authentication.service.CustomClientDetailsService;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.web.authority.AuthorityFactory;
import com.piece.core.web.cookie.CookieUtil;
import com.piece.core.web.util.AuthenticationUtil;
import com.piece.core.web.util.ServletUtil;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RefreshScope
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Value("${spring.security.session.expire:30}")
    private int expire = 30;

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Resource
    private PasswordEncoder passwordEncoder;

    @Lazy
    @Resource
    private TokenServices tokenServices;

    @Lazy
    @Resource
    private CustomClientDetailsService clientDetailsService;

    @Resource
    private AuthorityFactory authorityFactory;

    public AuthenticationSuccessHandler() {
        super();
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        DefaultSavedRequest savedRequest = (DefaultSavedRequest) this.requestCache.getRequest(request, response);
        if (null == savedRequest || savedRequest.getRequestURI().indexOf("oauth/authorize") == -1) {
            String clientHeader = request.getHeader(HeaderConstants.Authorization);
            String[] client = this.authorityFactory.get().extractClient(request);
            if (null == client) {
                throw new UnapprovedClientAuthenticationException(I18nUtil.message(ExceptionAuthConstants.CLIENT_INVALID));
            }

            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(client[0]);
            if (null == client) {
                throw new UnapprovedClientAuthenticationException(I18nUtil.message(ExceptionAuthConstants.CLIENT_INVALID));
            } else if (!passwordEncoder.matches(client[1], clientDetails.getClientSecret())) {
                throw new UnapprovedClientAuthenticationException(I18nUtil.message(ExceptionAuthConstants.PASSWORD_NOMATCH));
            }

            OAuth2AccessToken token;
            try {
                TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, client[0], clientDetails.getScope(), SecurityConstants.USERNAME_CODE);
                OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
                OAuth2Authentication oauth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
                token = tokenServices.createAccessToken(oauth2Authentication);
                AuthenticationUtil.getInstance(request).loginSuccessLog((String) authentication.getPrincipal(), request.getParameterMap());
            } catch (AuthenticationException e) {
                ServletUtil.renderString(response, JsonUtil.toJson(AjaxResponse.error(e.getMessage())));
                return;
            }

            CookieUtil.setCookie(request, response, CookieConstants.SESSION_KEY, token.getValue(), expire * 60);
            CookieUtil.setCookie(request, response, CookieConstants.TENANT_KET, clientHeader);
            CookieUtil.setCookie(request, response, CookieConstants.TOKEN_TYPE, token.getTokenType());
            ServletUtil.renderString(response, JsonUtil.toJson(AjaxResponse.success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS), token)));
        } else {
            String targetUrlParameter = this.getTargetUrlParameter();
            if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
                this.clearAuthenticationAttributes(request);
                String targetUrl = savedRequest.getRedirectUrl();
                ServletUtil.renderString(response, JsonUtil.toJson(AjaxResponse.success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS), targetUrl)));
            } else {
                this.requestCache.removeRequest(request, response);
                super.onAuthenticationSuccess(request, response, authentication);
            }
        }
    }
}
